package com.xored.searchwt;

import java.lang.reflect.Method;

import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CLabel;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IKeyBindingService;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.part.MultiPageEditorPart;

/**
 * @author Yuri Strot
 *
 */
public class SearchControl {
	
	private Text fText;
	private ToolItem bNext, bPrev;
	private ToolItem prefix, matchCase;
	private ToolItem bHighlight;
	private CLabel matchCount;
	private SearchAgent agent;
	private boolean enabled;
	private boolean highlighted;
	private String initialText;
	
	private ISearchView view;
	
	private static final Color GOOD_COLOR = Display.getDefault().getSystemColor(SWT.COLOR_WHITE); 
	private static final Color BAD_COLOR = new Color(Display.getDefault(), 255, 102, 102);
	
	public void initAgent(ISearchView view) {
		this.view = view;
		agent = new SearchAgent(view);
		agent.init();
	}
	
	public void create(Composite parent) {
		fText = new Text(parent, SWT.BORDER | SWT.SEARCH);
		view.initTextLayout(parent, fText);
		fText.addModifyListener(new ModifyListener() {
		
			public void modifyText(ModifyEvent e) {
				if (enabled)
					updateButtons(fText.getText().length() != 0);
				if (agent != null) {
					boolean found = agent.find(fText.getText(), SearchAgent.FIND_HERE);
					fText.setBackground(found ? GOOD_COLOR : BAD_COLOR);
				}
			}
		
		});
		
		fText.addKeyListener(new KeyListener() {
		
			public void keyReleased(KeyEvent e) {
			}
		
			public void keyPressed(KeyEvent e) {
				if (e.keyCode == '\r') {
					if (e.stateMask == 0)
						findNext();
					else if (e.stateMask == SWT.SHIFT)
						findPrevious();
				}
				if (e.keyCode == SWT.ESC && e.stateMask == 0) {
					agent.backFocus();
					view.backFocus();
				}
			}
		
		});
		
		fText.addFocusListener(new FocusListener() {

			private IWorkbenchPart prevScopePart;
			private final String[] EMPTY_SCOPE = new String[0];
			private String[] prevScope = null;
		
			public void focusLost(FocusEvent e) {
				if (prevScopePart != null && prevScope != null) {
					IKeyBindingService service = prevScopePart.getSite().getKeyBindingService();
					service.setScopes(prevScope);
				}
			}
		
			public void focusGained(FocusEvent e) {
				prevScopePart = getPart();
				if (prevScopePart != null) {
					IKeyBindingService service = prevScopePart.getSite().getKeyBindingService();
					prevScope = service.getScopes();
					service.setScopes(EMPTY_SCOPE);
				}
			}
			
			private IWorkbenchPart getPart() {
				IWorkbenchWindow window = view.getWindow();
				if (window != null) {
					IWorkbenchPage page = window.getActivePage();
					if (page != null) {
						IWorkbenchPart part = page.getActivePart();
						if (part instanceof MultiPageEditorPart)
							part = getActiveMultiPage((MultiPageEditorPart)part);
						return part;
					}
				}
				return null;
			}
			
			private IWorkbenchPart getActiveMultiPage(MultiPageEditorPart part) {
				try {
					Method method = part.getClass().getMethod("getActiveEditor", new Class[] {});
					return (IEditorPart)method.invoke(part, new Object[] {});
				} catch (Exception e) {
					e.printStackTrace();
				}
				return null;
				
			}
		
		});
		
		ToolBar bar = new ToolBar(parent, SWT.FLAT);
		bNext = new ToolItem(bar, SWT.PUSH);
		bNext.setToolTipText("Next");
		bNext.setImage(Activator.getImage("icons/next.gif"));
		bNext.addSelectionListener(new SelectionListener() {
		
			public void widgetSelected(SelectionEvent e) {
				findNext();
			}
		
			public void widgetDefaultSelected(SelectionEvent e) {
				findNext();
			}
		
		});
		
		bPrev = new ToolItem(bar, SWT.PUSH);
		bPrev.setToolTipText("Previous");
		bPrev.setImage(Activator.getImage("icons/prev.gif"));
		bPrev.addSelectionListener(new SelectionListener() {
			
			public void widgetSelected(SelectionEvent e) {
				findPrevious();
			}
		
			public void widgetDefaultSelected(SelectionEvent e) {
				findPrevious();
			}
		
		});
		
		matchCase = new ToolItem(bar, SWT.CHECK);
		matchCase.setImage(Activator.getImage("icons/match_case.png"));
		matchCase.setToolTipText("Match case");
		matchCase.addSelectionListener(new SelectionListener() {
		
			public void widgetSelected(SelectionEvent e) {
				setAgentParam(matchCase, SearchAgent.MATCH_CASE);
			}
		
			public void widgetDefaultSelected(SelectionEvent e) {
				setAgentParam(matchCase, SearchAgent.MATCH_CASE);
			}
		
		});
		
		prefix = new ToolItem(bar, SWT.CHECK);
		prefix.setImage(Activator.getImage("icons/whole_word.gif"));
		prefix.setToolTipText("Word prefix");
		prefix.addSelectionListener(new SelectionListener() {
		
			public void widgetSelected(SelectionEvent e) {
				setAgentParam(prefix, SearchAgent.PREFIX);
			}
		
			public void widgetDefaultSelected(SelectionEvent e) {
				setAgentParam(prefix, SearchAgent.PREFIX);
			}
		
		});
		
		bHighlight = new ToolItem(bar, SWT.CHECK);
		bHighlight.setImage(Activator.getImage("icons/highlight.png"));
		bHighlight.setToolTipText("Highlight");
		bHighlight.addSelectionListener(new SelectionListener() {
		
			public void widgetSelected(SelectionEvent e) {
				setAgentParam(bHighlight, SearchAgent.HIGHLIGHT);
			}
		
			public void widgetDefaultSelected(SelectionEvent e) {
				setAgentParam(bHighlight, SearchAgent.HIGHLIGHT);
			}
		
		});
		
		matchCount = new CLabel(parent, SWT.CENTER);
		matchCount.setText("      ");
		matchCount.setToolTipText("Match Count");
		view.initLabelLayout(parent, matchCount);
		
		initPrefState();
	}
	
	protected void setAgentParam(ToolItem item, String param) {
		boolean value = item.getSelection();
		if (agent != null)
			agent.set(param, value);
		setPrefState(param, value);
	}
	
	protected void setPrefState(String name, boolean value) {
		IPreferenceStore stroke = Activator.getDefault().getPreferenceStore();
		stroke.setValue(name, value);
	}
	
	protected void initPrefState() {
		IPreferenceStore stroke = Activator.getDefault().getPreferenceStore();
		matchCase.setSelection(stroke.getBoolean(SearchAgent.MATCH_CASE));
		setAgentParam(matchCase, SearchAgent.MATCH_CASE);
		
		prefix.setSelection(stroke.getBoolean(SearchAgent.PREFIX));
		setAgentParam(prefix, SearchAgent.PREFIX);
		
		bHighlight.setSelection(stroke.getBoolean(SearchAgent.HIGHLIGHT));
		setAgentParam(bHighlight, SearchAgent.HIGHLIGHT);
	}
	
	public void update(IWorkbenchPart part, boolean highlight, String text) {
		enabled = part != null;
		this.highlighted = highlight;
		fText.setEnabled(enabled);
		updateButtons(enabled);
		if (text != null) {
			initialText = text;
			tryUpdateText();
		}
	}
	
	public void updateHighlightCount(int count) {
		if (matchCount != null && !matchCount.isDisposed()) {
			if (count < 0)
				matchCount.setVisible(false);
			else {
				matchCount.setText(count + "");
				matchCount.setVisible(true);
			}
		}
	}
	
	private void tryUpdateText() {
		if (fText != null && !fText.isDisposed()) {
			fText.setText(initialText);
			initialText = null;
		}
	}
	
	private void updateButtons(boolean enable) {
		bNext.setEnabled(enable);
		bPrev.setEnabled(enable);
		bHighlight.setEnabled(highlighted);
	}
	
	private void findNext() {
		if (agent != null) {
			boolean found = agent.find(fText.getText(), SearchAgent.FIND_NEXT);
			fText.setBackground(found ? GOOD_COLOR : BAD_COLOR);
		}
	}
	
	private void findPrevious() {
		if (agent != null) {
			boolean found = agent.find(fText.getText(), SearchAgent.FIND_PREVIOUS);
			fText.setBackground(found ? GOOD_COLOR : BAD_COLOR);
		}
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.ui.part.WorkbenchPart#setFocus()
	 */
	public void setFocus() {
		if (initialText != null) {
			tryUpdateText();
		}
		acticate();
	}
	
	public void acticate() {
		if (fText != null && !fText.isDisposed()) {
			fText.selectAll();
			fText.setFocus();
		}
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.ui.part.WorkbenchPart#dispose()
	 */
	public void dispose() {
		if (agent != null) {
			agent.dispose();
			agent = null;
		}
	}

}
